package cn.yangliu.nacos.gateway.filter;

import java.util.List;
import java.util.Objects;
import java.util.Set;

import javax.annotation.PostConstruct;

import cn.yangliu.nacos.comm.JsonResult;
import cn.yangliu.nacos.comm.constant.SecurityConstants;
import cn.yangliu.nacos.comm.enums.GlobalResultCode;
import cn.yangliu.nacos.gateway.config.GatewayProperties;
import cn.yangliu.nacos.gateway.model.Oauth2TokenInfo;
import cn.yangliu.nacos.gateway.service.Oauth2ClientService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.ByteBufFlux;

/**
 * The interface Global authority filter.
 *
 * @author 问道于盲
 * @date 2019 -12-14
 */
@Component
public class GlobalAuthorityFilter implements GlobalFilter {

    /**
     * current class instance's member.
     * The Properties.
     */
    @Autowired
    private GatewayProperties properties;

    /**
     * current class instance's member.
     * The Oauth 2 client service.
     */
    @Autowired
    private Oauth2ClientService oauth2ClientService;


    /**
     * current class instance's member.
     * The Om.
     */
    private ObjectMapper om = new ObjectMapper();

    private AntPathMatcher pathMatcher = new AntPathMatcher();

    /**
     * current class instance's member.
     * The Anonmymous urls.
     */
    private List<String> anonmymousUrls;

    /**
     * Init.
     */
    @PostConstruct
    public void init() {
        anonmymousUrls = properties.getAnonmymousUrls();
        //请求token的接口 直接放行
        anonmymousUrls.add("/oauth2/**");
        anonmymousUrls.add("/**/v2/api-docs");
    }

    /**
     * Filter mono.
     *
     * @param exchange the exchange
     * @param chain    the chain
     * @return the mono
     */
    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        assert Objects.nonNull(route);
        String serviceName = route.getUri().getHost();

        String path = "/" + serviceName + request.getPath().value();
        //放行
        for (String anonmymousUrl : anonmymousUrls) {
            if (pathMatcher.match(anonmymousUrl,path)){
                return chain.filter(exchange);
            }
        }

        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = request.getHeaders();
        List<String> authorization = headers.get("Authorization");

        //判断是否包含Authorization
        if (CollectionUtils.isEmpty(authorization)) {
            JsonResult unauthorized = JsonResult.getInstance(GlobalResultCode.MISS_TOKEN);

            byte[] bytes = om.writeValueAsBytes(unauthorized);
            //设置http响应状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //设置响应头信息Content-Type类型
            response.getHeaders().add("Content-Type", "application/json");
            //设置返回json数据
            return response.writeAndFlushWith(
                    Flux.just(ByteBufFlux.just(response.bufferFactory().wrap(bytes))));
        }

        String token = authorization.get(0);
        String tempToken = token.toLowerCase();
        String authorizationBearer = SecurityConstants.AUTHORIZATION_BEARER.toLowerCase();

        //判断token类型是不是Bearer
        if (!tempToken.contains(authorizationBearer)) {
            JsonResult unauthorized = JsonResult.getInstance(GlobalResultCode.TOKEN_ILLEGAL);

            byte[] bytes = om.writeValueAsBytes(unauthorized);
            //设置http响应状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            //设置响应头信息Content-Type类型
            response.getHeaders().add("Content-Type", "application/json");
            //设置返回json数据
            return response.writeAndFlushWith(
                    Flux.just(ByteBufFlux.just(response.bufferFactory().wrap(bytes))));
        }
        token = token.substring(token.lastIndexOf(" ") + 1);

        Oauth2TokenInfo oauth2TokenInfo = oauth2ClientService.clientInfo(token);

        Set<String> authorities =
                oauth2ClientService.findOauth2ClientAuthorityByClientId(oauth2TokenInfo.getClientId());

        //判断是否有访问该服务的权限
        if (!authorities.contains(serviceName)) {
            JsonResult forbidden = JsonResult.getInstance(HttpStatus.FORBIDDEN.value(), "forbidden");

            byte[] bytes = om.writeValueAsBytes(forbidden);
            //设置http响应状态码
            response.setStatusCode(HttpStatus.FORBIDDEN);
            //设置响应头信息Content-Type类型
            response.getHeaders().add("Content-Type", "application/json");
            //设置返回json数据
            return response.writeAndFlushWith(
                    Flux.just(ByteBufFlux.just(response.bufferFactory().wrap(bytes))));
        }

        return chain.filter(exchange);
    }
}
